package com.tarena.csmall.order.web.consumer;

import cn.tedu.mall.pojo.seckill.dto.SeckillOrderAddDTO;
import com.tarena.csmall.order.web.service.SeckillOrderService;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

/**
 * consumer生效 三步
 * 1. 注册到spring容器
 * 2. 实现接口
 * 3. 实现监听注解
 */
@Component
/**
 * 监听的主题,
 * seckill_order_topic
 * 分组已经在yaml中配置了
 * 消费这个主题中所有tag消息
 */
@RocketMQMessageListener(
    topic="seckill_order_topic",consumerGroup = "${rocketmq.consumer.group}",
    selectorExpression = "*")
@Slf4j
public class OrderAddConsumer implements RocketMQListener<SeckillOrderAddDTO> {
    @Autowired
    private SeckillOrderService seckillOrderService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override public void onMessage(SeckillOrderAddDTO dto) {
        //在执行消费逻辑之前,要先对数据做抢锁,抢到锁的执行业务逻辑
        //没抢到的,等待5秒钟,重新抢锁.最多抢3次,3次都没抢到,业务方法就不执行了.在执行业务方法.
        //锁的key值,使用sn.
        String lockKey="seckill:order:sn:lock:"+dto.getSn();
        String randCode=new Random().nextInt(9000)+1000+"";
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        //抢锁
        Boolean haveLock = opsForValue.setIfAbsent(lockKey, randCode, 10, TimeUnit.SECONDS);
        try {
            if (!haveLock) {
                int count = 0;//记录抢锁次数
                while (count < 3) {
                    //没抢到,等待5秒钟,重新抢锁
                    Thread.sleep(5000);
                    //重新抢锁
                    haveLock = opsForValue.setIfAbsent(lockKey, randCode, 10, TimeUnit.SECONDS);
                    if (haveLock) {
                        //退出,并且执行业务逻辑
                        seckillOrderService.orderAdd(dto);
                        break;
                    }
                    count++;
                }
            } else {
                seckillOrderService.orderAdd(dto);
                //业务逻辑 待补充,先验证一下 是否能够消费到消息
                log.info("消费端接收到消息:{}", dto);
            }
        }catch (Exception e){
            log.error("抢锁执行业务方法异常:{}",e.getMessage(),e);
        }finally {
            //释放锁
            if(randCode.equals(opsForValue.get(lockKey))){
                stringRedisTemplate.delete(lockKey);
            }
        }
    }
}
